//
//  RecorderManager.mm
//  OggSpeex
//
//  Created by Jiang Chuncheng on 6/25/13.
//  Copyright (c) 2013 Sense Force. All rights reserved.
//

#import "RecorderManager.h" 
#import "amrFileCodec.h"
#import "HRAudioPath.h"
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>


@interface RecorderManager()
/** 录音机 */
@property (nonatomic, strong) AVAudioRecorder *audioRecorder;

/** 当前是否正在录音*/
@property(nonatomic,assign) BOOL isRecording;

@end

@implementation RecorderManager

@synthesize dateStartRecording, dateStopRecording;
//@synthesize encapsulator;
@synthesize timerLevelMeter;
@synthesize timerTimeout;

static RecorderManager *mRecorderManager = nil;
//AQRecorder *mAQRecorder;
AudioQueueLevelMeterState *levelMeterStates;

+ (RecorderManager *)sharedManager {
    @synchronized(self) {
        if (mRecorderManager == nil) {
            mRecorderManager = [[self alloc] init];
        }
    }
    return mRecorderManager;
}

+ (id)allocWithZone:(NSZone *)zone {
    @synchronized(self) {
        if(mRecorderManager == nil) {
            mRecorderManager = [super allocWithZone:zone];
            return mRecorderManager;
        }
    }
    
    return nil;
}



#pragma mark - Public Function

- (BOOL)isRecordAvailabe {
    __block BOOL open = NO;
    AVAudioSession *session = [AVAudioSession sharedInstance];
    if ([session respondsToSelector:@selector(requestRecordPermission:)]) {
        [session performSelector:@selector(requestRecordPermission:) withObject:^(BOOL status) {
            open = status;
        }];
    } else {
        open = YES;
    }
    
    return open;
}

- (void)startRecording {
    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    // 设置为播放和录音状态，以便可以在录制完之后播放录音
    [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
    [audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
    [audioSession setActive:YES error:nil];
    
    /**
     *  Speex 格式录音，具有消除回音功能。因android暂时不支持，故使用通用amr格式录音
     *
    if (!mAQRecorder) {
        mAQRecorder = new AQRecorder();
        
        OSStatus error = AudioSessionInitialize(NULL, NULL, interruptionListener, (__bridge void *)self);
        if (error) printf("ERROR INITIALIZING AUDIO SESSION! %d\n", (int)error);
        else
        {
            UInt32 category = kAudioSessionCategory_PlayAndRecord;
            error = AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(category), &category);
            if (error) printf("couldn't set audio category!");
            
            error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange, propListener, (__bridge void *)self);
            if (error) printf("ERROR ADDING AUDIO SESSION PROP LISTENER! %d\n", (int)error);
            UInt32 inputAvailable = 0;
            UInt32 size = sizeof(inputAvailable);
            
            // we do not want to allow recording if input is not available
            error = AudioSessionGetProperty(kAudioSessionProperty_AudioInputAvailable, &size, &inputAvailable);
            if (error) printf("ERROR GETTING INPUT AVAILABILITY! %d\n", (int)error);
            
            // we also need to listen to see if input availability changes
            error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioInputAvailable, propListener, (__bridge void *)self);
            if (error) printf("ERROR ADDING AUDIO SESSION PROP LISTENER! %d\n", (int)error);
            
            error = AudioSessionSetActive(true); 
            if (error) printf("AudioSessionSetActive (true) failed");
        }
        
    }
    
    saveFileName = [NSString stringWithString:[Encapsulator defaultFileName]];
    NSLog(@">>> recorder saveFileName :%@",saveFileName);
    
    
    if ( ! self.encapsulator) {
        self.encapsulator = [[Encapsulator alloc] initWithFileName:saveFileName];
        self.encapsulator.delegete = self;
    }
    else {
        [self.encapsulator resetWithFileName:saveFileName];
    }
    if ( ! mAQRecorder->IsRunning()) {
        mAQRecorder->StartRecord(self.encapsulator);
    }
    if ( ! mAQRecorder->IsRunning()) {
        mAQRecorder->StartRecord(self.encapsulator);
        
        NSLog(@"audio session category : %@", [[AVAudioSession sharedInstance] category]);
        Boolean recordingWillBegin = mAQRecorder->StartRecord(encapsulator);
        if ( ! recordingWillBegin) {
            if ([self.delegate respondsToSelector:@selector(recordingFailed:)]) {
                [self.delegate recordingFailed:@"程序错误，无法继续录音，请重启程序试试"];
            }
            return;
        }
    }
     
    *******/
    
    // 创建录音文件保存路径
    saveFileName   = [HRAudioPath recordPathOrigin];
    NSURL *fileURL = [NSURL URLWithString:saveFileName];

    if (!_audioRecorder) {
        // 创建录音格式设置
        NSDictionary *setting = [self getAudioSetting];
        
        // 创建录音机
        NSError *error = nil;
        _audioRecorder = [[AVAudioRecorder alloc] initWithURL:fileURL settings:setting error:&error];
        _audioRecorder.meteringEnabled = YES;// 如果要监控声波则必须设置为YES
        
        if (error) {
            NSLog(@">>> 创建录音机对象时发生错误，错误信息：%@", error.localizedDescription);
        }
    }
    
    // 准备开始录音
    [self.audioRecorder prepareToRecord]; 
    [self.audioRecorder record];
    self.dateStartRecording = [NSDate date];
    self.isRecording = YES;
    
    if (!levelMeterStates) {
        levelMeterStates = (AudioQueueLevelMeterState *)malloc(sizeof(AudioQueueLevelMeterState) * 1);
    }
    
    self.timerLevelMeter = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(updateLevelMeter:) userInfo:nil repeats:YES];
    self.timerTimeout = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:@selector(timeoutCheck:) userInfo:nil repeats:NO];
}

- (void)stopRecording {
    [self stopRecording:NO];
}

- (void)cancelRecording {
    [self stopRecording:YES];
}

- (NSTimeInterval)recordedTimeInterval {
    NSLog(@">>> record start time %@______ record stop time %@",dateStartRecording,dateStopRecording);
    return (dateStopRecording && dateStartRecording) ? [dateStopRecording timeIntervalSinceDate:dateStartRecording] : 0;
}

#pragma mark - Action
/**
 *  录音声波状态设置
 */
- (void)updateLevelMeter:(id)sender {
    if (self.delegate) {
        /**
         *  Speex
         *
         UInt32 dataSize = sizeof(AudioQueueLevelMeterState);
         AudioQueueGetProperty(mAQRecorder->Queue(), kAudioQueueProperty_CurrentLevelMeter, levelMeterStates, &dataSize);
         if ([self.delegate respondsToSelector:@selector(levelMeterChanged:)]) {
         [self.delegate levelMeterChanged:levelMeterStates[0].mPeakPower];
         } ***/
        
        [self.audioRecorder updateMeters]; // 更新测量值
        float power = pow(10, (0.05 * [self.audioRecorder peakPowerForChannel:0]));
        
        NSLog(@"录音大小: %f", power);
        
        if ([self.delegate respondsToSelector:@selector(recorderManager:levelMeterChanged:)]) {
            [self.delegate recorderManager:self levelMeterChanged:power];
        }
    }
}

- (void)timeoutCheck:(id)sender {
    if ([self.delegate respondsToSelector:@selector(recorderManagerDidTimeout:)]) {
        [self.delegate recorderManagerDidTimeout:self];
    }
}


#pragma mark - Private Function
/**
 *  停止录音
 *
 *  @param isCanceled 是否取消这个录音
 */
- (void)stopRecording:(BOOL)isCanceled {
    
    self.dateStopRecording = [NSDate date];
    self.isRecording = NO;
    
    [self.timerLevelMeter invalidate];
    [self.timerTimeout invalidate];
    self.timerLevelMeter = nil;
    
    if (self.delegate) {
        if ([self.delegate respondsToSelector:@selector(recorderManagerDidStopped:)]) {
            [self.delegate recorderManagerDidStopped:self];
        }
    }
    
    // 取消录音
    if (isCanceled) {
        if (self.audioRecorder.isRecording) {
            NSURL *url = [[NSURL alloc] initWithString:saveFileName];
            
            [self.audioRecorder stop];
            self.audioRecorder = nil;
            [[NSFileManager defaultManager] removeItemAtURL:url error:nil];
        }
        
    }else {
        // 停止录音
        [self.audioRecorder stop];
        self.audioRecorder = nil;
        
        if ([[NSFileManager defaultManager] fileExistsAtPath:saveFileName]) {
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                
                NSData *wavData = [NSData dataWithContentsOfFile:saveFileName];
                NSData *amrData = EncodeWAVEToAMR(wavData, 1, 16);
                
                NSString *amrPath = [HRAudioPath recordPathOriginToAMR];
                NSURL    *urlSave = [NSURL fileURLWithPath:amrPath];
                
                if ([amrData writeToURL:urlSave atomically:YES]) {
                    NSLog(@">>> 保存录音成功 ：%@", amrPath);
                }
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    if ([self.delegate respondsToSelector:@selector(recorderManager:didFinishedWithFileName:time:)]) {
                        [self.delegate recorderManager:self didFinishedWithFileName:amrPath time:[self recordedTimeInterval]];
                    }
                });
            });
        }
    }
}



/**
 *  取得录音文件设置
 *
     AVFormatIDKey  音乐格式，这里采用PCM格式
     AVSampleRateKey 设置录音采样率，8000是电话采样率，对于一般录音已经够了
     AVNumberOfChannelsKey 音乐通道数
     AVLinearPCMBitDepthKey,采样位数 默认 16
     AVLinearPCMIsFloatKey,采样信号是整数还是浮点数
     AVLinearPCMIsBigEndianKey,大端还是小端 是内存的组织方式
     AVEncoderAudioQualityKey,音频编码质量
 *
 *
 *  @return 录音设置
 */
- (NSDictionary *)getAudioSetting { 
    NSDictionary *recordSetting = @{AVFormatIDKey               : @(kAudioFormatLinearPCM),
                                    AVSampleRateKey             : @(8000.f),
                                    AVNumberOfChannelsKey       : @(1),
                                    AVLinearPCMBitDepthKey      : @(16),
                                    AVLinearPCMIsNonInterleaved : @(NO),
                                    AVLinearPCMIsFloatKey       : @(YES),
                                    AVLinearPCMIsBigEndianKey   : @(YES)
                                    };
    return recordSetting;
}


@end
